#include <iostream>
#include <fstream>
#include <iomanip>
#include "header.h"

using namespace std;

int AND( unsigned short inst )
{
  unsigned short EAddr;
  EAddr = calEAddr( inst );

  LB_AC = LB_AC & readMEM(EAddr);

  return 0;
}

int TAD( unsigned short inst )
{
  unsigned short EAddr;
  unsigned short mask = 017777;

  EAddr = calEAddr( inst );

  LB_AC = LB_AC + readMEM(EAddr);
  
  LB_AC = LB_AC & mask;

  return 0;
}

int ISZ( unsigned short inst )
{
  unsigned short EAddr;
  unsigned short tmp;
  
  tmp = 0;

  EAddr = calEAddr( inst );

  tmp = readMEM(EAddr) + 00001;

  tmp &= 007777;

  writeMEM(EAddr, tmp);
  

  if( tmp == 00 )
    PC = PC + 01;
  

  return 0;
}

int DCA( unsigned short inst )
{
  unsigned short EAddr;
  unsigned short mask1 = 007777;
  unsigned short mask2 = 010000;
  unsigned short data;

  EAddr = calEAddr( inst );

  data = LB_AC & mask1;

  writeMEM(EAddr,data);
  
  LB_AC = LB_AC & mask2;

  return 0;
}

int JMS( unsigned short inst )
{
  unsigned short EAddr;

  EAddr = calEAddr( inst );

  writeMEM(EAddr,PC);

  PC = EAddr + 01;

  return 0;
}

int JMP( unsigned short inst )
{
  unsigned short EAddr;

  EAddr = calEAddr( inst );

  PC = EAddr;

  return 0;
}

unsigned short calEAddr(unsigned short inst)
{
	unsigned short EAddr;
	unsigned short offset;
	unsigned short CPC = PC - 1;

	EAddr = 0;
	offset = inst & 00177;

	switch (inst & 00600)
	{
		case 00000:	// Zero Page Addressing OR Autoindexing
					EAddr |= offset; // Zero Page Addressing

					break;

		case 00200: // Current Page Addressing
					EAddr = (CPC & 07600) | offset;	// Mask low order 7 bits of CPC
													// then concatenate with offset
					break;

		case 00400: // Indirect Addressing with Zero page
					if (offset >= 00010 && offset <= 00017) // Autoiindexing
					{
						EAddr = readMEM(offset) + 1;
						writeMEM(offset, EAddr);
					}
					else
						EAddr = readMEM(offset);
					break;

		case 00600: // Indirect Addressing with Current page
					EAddr = readMEM((CPC & 07600) | offset);
					break;
					
		default:;
	}

	if(debug)
	{
		if(!debugfile)
		{
			cout << "%%%%%%%%%%%%%%%%%%%\n";
			cout << " EAddr Calculation\n";
			cout << "%%%%%%%%%%%%%%%%%%%\n";
			cout << "  EAddr :";
			cout << setfill('0') << setw(4) << oct << EAddr << endl;
		}
		else
		{
			ofstream fout;

			fout.open("I:\\ECE486\\Project1\\testoutput.txt",fstream::app);	//append testoutput.txt

			fout << endl;
			fout << "%%%%%%%%%%%%%%%%%%%\n";
			fout << " EAddr Calculation\n";
			fout << "%%%%%%%%%%%%%%%%%%%\n";
			fout << "  EAddr :";
			fout << setfill('0') << setw(4) << oct << EAddr << endl;
		}
	}

	return EAddr;
}

//int updateExtraMemCyc( unsigned short inst )
//{
//	unsigned short offset;
//
//	offset = inst & 00177;
//
//
//	switch (inst & 00600)
//	{
//		case 00000:	// Zero Page Addressing OR Autoindexing
//					if (offset >= 00010 && offset <= 00017) // Autoiindexing
//						extraMemCycCount += 2;
//					else
//					break;
//
//		case 00400:
//		case 00600:
//					extraMemCycCount += 1;
//					break;
//		default:;
//	}
//
//  return 0;
//}

int createTrace(int mode, unsigned short memAddr)
{
	ofstream fout;

	if(debug)
	{
		char mode_name[10];
		switch (mode)
		{
			case FETCH: strcpy_s(mode_name, "Fetch"); break;
			case READ: strcpy_s(mode_name, "Read"); break;
			case WRITE: strcpy_s(mode_name, "Write"); break;
		}

		if(!debugfile)
		{
			cout << "\n**************************\n";
			cout << " Memory Access  " << endl;
			cout << "**************************\n";
			cout << "  Mode        :" << mode_name << ' ';
			cout << setfill('0') << setw(4) << oct << memAddr << endl;
			cout << "  memory[" << setfill('0') << setw(4) << memAddr << "] :";
			cout << setfill('0') << setw(4) << oct << memory[ memAddr ] << endl;
		}
		else
		{
			//ofstream fout;

			//fout.open("C:\\Users\\Eugene\\Documents\\My Classes\\ECE486\\ece486projectsvn\\codes\\current\\testoutput.txt",fstream::app);	//append testoutput.txt

			//fout << "**************************\n";
			//fout << " Memory Access  " << endl;
			//fout << "**************************\n";
			//fout << "  Mode        :" << mode_name << ' ';
			//fout << setfill('0') << setw(4) << oct << memAddr << endl;
			//fout << "  memory[" << setfill('0') << setw(4) << memAddr << "] :";
			//fout << setfill('0') << setw(4) << oct << memory[ memAddr ] << endl;
		}
	}

	char *traceFileName = new char [strlen(objFilePath)+3];
	strcpy_s(traceFileName, strlen(objFilePath)+1, objFilePath);
	traceFileName[strlen(objFilePath)-3] = 't'; //substitute .as 
	traceFileName[strlen(objFilePath)-2] = 'r'; //with .trac
	traceFileName[strlen(objFilePath)-1] = 'a';
	traceFileName[strlen(objFilePath)] = 'c';
	traceFileName[strlen(objFilePath)+1] = 'e';
	traceFileName[strlen(objFilePath)+2] = 0;

	fout.open(traceFileName,fstream::app);	//append pdp8tracec.oct
	//fout.open("I:\\ECE486\\Project1\\testtrace.oct",fstream::app);	//append pdp8tracec.oct

	fout << oct;
	fout << mode << ' ' << setfill('0') << setw(4) << memAddr << endl;

	fout.close();

	return 0;

}

unsigned short readMEM(unsigned short EAddr)
{
	unsigned short data;

	data = memory[EAddr];
	
	++memReadCount;

	createTrace( READ, EAddr );

	return data;

}

unsigned short writeMEM(unsigned short EAddr, unsigned short data)
{
	memory[EAddr] = data & 007777;
	
	++memWriteCount;

	createTrace( WRITE, EAddr );

	return 0;
}